public class CacheLookup
{
private readonly Dictionary<string,int> _map = new();
public ValueTask<int> GetAsync(string key)
{
if (_map.TryGetValue(key, out var v))
return new ValueTask<int>(v); // 同步快路徑,不配置 Task
return new ValueTask<int>(SlowPathAsync(key)); // 退化為 Task
}
private async Task<int> SlowPathAsync(string key)
{
await Task.Delay(5); // 模擬 I/O
int value = key.Length;
_map[key] = value;
return value;
}
}
若約九成呼叫命中快取,可明顯降低配置與 GC;命中率偏低(例如四成以下)改用 Task 可讀性與維護性較佳。
using System;
using System.Threading.Tasks.Sources;
sealed class PooledOp : IValueTaskSource<int>
{
private ManualResetValueTaskSourceCore<int> _core;
public short Version => _core.Version;
public void Start()
{
// 啟動非同步作業(例如註冊 socket 回呼)
// 完成時呼叫 SetResult 或 SetException
}
public void SetResult(int value) => _core.SetResult(value);
public void SetException(Exception ex) => _core.SetException(ex);
public int GetResult(short token) => _core.GetResult(token);
public ValueTaskSourceStatus GetStatus(short token) => _core.GetStatus(token);
public void OnCompleted(Action<object?> c, object? state, short token, ValueTaskSourceOnCompletedFlags flags)
=> _core.OnCompleted(c, state, token, flags);
public ValueTask<int> AsValueTask() => new(this, Version);
public void Reset() => _core.Reset(); // 歸還前重置
}
重點:物件歸還池前務必已完成並呼叫 Reset;未完成即重用會產生競態與錯誤結果。
快速檢查:
- | 核心意義 |
---|---|
狀態機 MoveNext | async 方法實際控制流程 |
Builder.SetResult / SetException | 終結與填入結果 |
Continuation | 等待完成後要執行的委派 |
TaskScheduler | 決定延續執行位置與策略 |
ThreadPool 工作竊取 | 本地 LIFO,跨執行緒竊尾端減少爭用 |
ConfigureAwait(false) | 不回傳 SynchronizationContext(保留 ExecutionContext) |
ValueTask | 高同步完成率、單次 await 場景減少配置 |
IValueTaskSource | 可重用零額外配置非同步來源 |
UnsafeOnCompleted | 不捕捉 ExecutionContext,僅限可信內部 |
Starvation | ThreadPool 無足夠 worker 處理排隊工作 |